package com.hyzx.qbasic.generator.config;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by 黄江华 on 2019/1/31
 */

@Configuration
public class AutoGeneratorConfig {
    private static final String ROOT_OUTPUT_PATH = System.getProperty("user.dir")
            + "/qbasic-generator/target/generated-code";

    private static final String JAVA_CODE_OUTPUT_PATH = ROOT_OUTPUT_PATH + "/src/main/java";

    private static final String RESOURCE_OUTPUT_PATH = ROOT_OUTPUT_PATH + "/src/main/resources";

    private static final String MAPPER_XML_OUTPUT_PATH = RESOURCE_OUTPUT_PATH + "/mapper/";

    private static final String HTML_OUTPUT_PATH = RESOURCE_OUTPUT_PATH + "/html/";

    private static final String JS_OUTPUT_PATH = RESOURCE_OUTPUT_PATH + "/js/";

    @Autowired
    private AppConfig appConfig;

    /**
     * 代码生成器
     */
    @Bean
    public AutoGenerator autoGenerator() {
        AutoGenerator autoGenerator = new AutoGenerator();
        autoGenerator.setGlobalConfig(globalConfig());
        autoGenerator.setDataSource(dataSourceConfig());
        autoGenerator.setPackageInfo(packageConfig());
        autoGenerator.setCfg(injectionConfig());
        autoGenerator.setTemplate(templateConfig());
        autoGenerator.setStrategy(strategyConfig());
        autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());

        return autoGenerator;
    }

    /**
     * 全局配置
     */
    @Bean
    public GlobalConfig globalConfig() {
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(JAVA_CODE_OUTPUT_PATH);
        gc.setFileOverride(true);
        gc.setOpen(true);
        gc.setEnableCache(false);
        gc.setAuthor(appConfig.getAuthor());
        gc.setKotlin(false);
        gc.setSwagger2(true);
        gc.setActiveRecord(false);
        gc.setBaseResultMap(false);
        gc.setDateType(DateType.ONLY_DATE);
        gc.setBaseColumnList(true);
        gc.setEntityName(appConfig.getEntityName() + "PO");
        gc.setMapperName(appConfig.getEntityName() + "Mapper");
        gc.setXmlName(appConfig.getEntityName() + "Mapper");
        gc.setServiceName("I" + appConfig.getEntityName() + "Service");
        gc.setServiceImplName(appConfig.getEntityName() + "ServiceImpl");
        gc.setControllerName(appConfig.getEntityName() + "Controller");
        return gc;
    }

    /**
     * 数据源配置
     */
    @Bean
    public DataSourceConfig dataSourceConfig() {
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(appConfig.getDbUrl());
        dsc.setDriverName(appConfig.getDriver());
        dsc.setUsername(appConfig.getDbUsername());
        dsc.setPassword(appConfig.getDbPassword());

        // 自定义数据库表字段类型转换
        dsc.setTypeConvert(new MySqlTypeConvert() {
            @Override
            public IColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) {
                if (fieldType.toLowerCase().contains("tinyint")) {
                    return DbColumnType.INTEGER;
                }

                return super.processTypeConvert(globalConfig, fieldType);
            }
        });

        return dsc;
    }

    /**
     * 包配置
     */
    @Bean
    public PackageConfig packageConfig() {
        PackageConfig pc = new PackageConfig();
        pc.setParent(appConfig.getBasePackage());
        pc.setEntity(appConfig.getEntityPackage());
        pc.setService(appConfig.getServicePackage());
        pc.setServiceImpl(appConfig.getServiceImplPackage());
        pc.setMapper(appConfig.getMapperPackage());
        pc.setController(appConfig.getControllerPackage());

        return pc;
    }

    /**
     * 自定义配置
     * 自定义配置会被优先输出
     */
    @Bean
    public InjectionConfig injectionConfig() {
        String repositoryPackage = appConfig.getBasePackage() + StringPool.DOT + appConfig.getRepositoryPackage();
        String repositoryImplPackage = appConfig.getBasePackage() + StringPool.DOT + appConfig.getRepositoryImplPackage();
        String repositoryName = "I" + appConfig.getEntityName() + "Repository";
        String repositoryImplName = appConfig.getEntityName() + "RepositoryImpl";

        String reqPackage = appConfig.getBasePackage() + StringPool.DOT + appConfig.getReqPackage();
        String resPackage = appConfig.getBasePackage() + StringPool.DOT + appConfig.getResPackage();
        String addReqName = "Add" + appConfig.getEntityName() + "Req";
        String editReqName = "Edit" + appConfig.getEntityName() + "Req";
        String deleteReqName = "Delete" + appConfig.getEntityName() + "Req";
        String getInfoReqName = "Get" + appConfig.getEntityName() + "InfoReq";
        String getListReqName = "Get" + appConfig.getEntityName() + "ListReq";

        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
                map.put("entityName", appConfig.getEntityName());
                map.put("repositoryPackage", repositoryPackage);
                map.put("repositoryImplPackage", repositoryImplPackage);
                map.put("repositoryName", repositoryName);
                map.put("repositoryImplName", repositoryImplName);
                map.put("reqPackage", reqPackage);
                map.put("resPackage", resPackage);
                map.put("addReqName", addReqName);
                map.put("editReqName", editReqName);
                map.put("deleteReqName", deleteReqName);
                map.put("getInfoReqName", getInfoReqName);
                map.put("getListReqName", getListReqName);
                map.put("routePath", appConfig.getRoutePath());
                map.put("searchField", appConfig.getSearchField());

                this.setMap(map);
            }
        };

        Map<String, String> params = new HashMap<>();

        // 自定义mapper.xml模板
        String xmlTemplatePath = "/tpl/mapper.xml.ftl";
        String xmlOutputPath = MAPPER_XML_OUTPUT_PATH
                + StringPool.SLASH + appConfig.getEntityName() + "Mapper" + StringPool.DOT_XML;
        params.put(xmlTemplatePath, xmlOutputPath);


        // 自定义Repository模板
        String repositoryTemplatePath = "/tpl/repository.java.ftl";
        String repositoryOutputPath = JAVA_CODE_OUTPUT_PATH + StringPool.SLASH
                + repositoryPackage.replace(StringPool.DOT, StringPool.SLASH)
                + StringPool.SLASH + repositoryName + StringPool.DOT_JAVA;
        params.put(repositoryTemplatePath, repositoryOutputPath);

        // 自定义RepositoryImpl模板
        String repositoryImplTemplatePath = "/tpl/repositoryImpl.java.ftl";
        String repositoryImplOutputPath = JAVA_CODE_OUTPUT_PATH + StringPool.SLASH
                + repositoryImplPackage.replace(StringPool.DOT, StringPool.SLASH)
                + StringPool.SLASH + repositoryImplName + StringPool.DOT_JAVA;
        params.put(repositoryImplTemplatePath, repositoryImplOutputPath);

        // 自定义AddReq模板
        String addReqTemplatePath = "/tpl/addReq.java.ftl";
        String addReqOutputPath = JAVA_CODE_OUTPUT_PATH + StringPool.SLASH
                + reqPackage.replace(StringPool.DOT, StringPool.SLASH)
                + StringPool.SLASH + addReqName + StringPool.DOT_JAVA;
        params.put(addReqTemplatePath, addReqOutputPath);

        // 自定义EditReq模板
        String editReqTemplatePath = "/tpl/editReq.java.ftl";
        String editReqOutputPath = JAVA_CODE_OUTPUT_PATH + StringPool.SLASH
                + reqPackage.replace(StringPool.DOT, StringPool.SLASH)
                + StringPool.SLASH + editReqName + StringPool.DOT_JAVA;
        params.put(editReqTemplatePath, editReqOutputPath);

        // 自定义DeleteReq模板
        String deleteReqTemplatePath = "/tpl/deleteReq.java.ftl";
        String deleteReqOutputPath = JAVA_CODE_OUTPUT_PATH + StringPool.SLASH
                + reqPackage.replace(StringPool.DOT, StringPool.SLASH)
                + StringPool.SLASH + deleteReqName + StringPool.DOT_JAVA;
        params.put(deleteReqTemplatePath, deleteReqOutputPath);

        // 自定义GetInfoReq模板
        String getInfoReqTemplatePath = "/tpl/getInfoReq.java.ftl";
        String getInfoReqOutputPath = JAVA_CODE_OUTPUT_PATH + StringPool.SLASH
                + reqPackage.replace(StringPool.DOT, StringPool.SLASH)
                + StringPool.SLASH + getInfoReqName + StringPool.DOT_JAVA;
        params.put(getInfoReqTemplatePath, getInfoReqOutputPath);

        // 自定义GetListReq模板
        String getListReqTemplatePath = "/tpl/getListReq.java.ftl";
        String getListReqOutputPath = JAVA_CODE_OUTPUT_PATH + StringPool.SLASH
                + reqPackage.replace(StringPool.DOT, StringPool.SLASH)
                + StringPool.SLASH + getListReqName + StringPool.DOT_JAVA;
        params.put(getListReqTemplatePath, getListReqOutputPath);

        // 自定义add.html模板
        String addHtmlTemplatePath = "/tpl/add.html.ftl";
        String addHtmlOutputPath = HTML_OUTPUT_PATH + StringPool.SLASH + "add.html";
        params.put(addHtmlTemplatePath, addHtmlOutputPath);

        // 自定义edit.html模板
        String editHtmlTemplatePath = "/tpl/edit.html.ftl";
        String editHtmlOutputPath = HTML_OUTPUT_PATH + StringPool.SLASH + "edit.html";
        params.put(editHtmlTemplatePath, editHtmlOutputPath);

        // 自定义list.html模板
        String listHtmlTemplatePath = "/tpl/list.html.ftl";
        String listHtmlOutputPath = HTML_OUTPUT_PATH + StringPool.SLASH + "list.html";
        params.put(listHtmlTemplatePath, listHtmlOutputPath);

        // 自定义add.js模板
        String addJSTemplatePath = "/tpl/add.js.ftl";
        String addJSOutputPath = JS_OUTPUT_PATH + StringPool.SLASH + "add.js";
        params.put(addJSTemplatePath, addJSOutputPath);

        // 自定义edit.js模板
        String editJSTemplatePath = "/tpl/edit.js.ftl";
        String editJSOutputPath = JS_OUTPUT_PATH + StringPool.SLASH + "edit.js";
        params.put(editJSTemplatePath, editJSOutputPath);

        // 自定义list.js模板
        String listJSTemplatePath = "/tpl/list.js.ftl";
        String listJSOutputPath = JS_OUTPUT_PATH + StringPool.SLASH + "list.js";
        params.put(listJSTemplatePath, listJSOutputPath);
        
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();

        params.forEach((templatePath, outputPath) -> {
            FileOutConfig fileOutConfig = new FileOutConfig(templatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名
                    return outputPath;
                }
            };

            focList.add(fileOutConfig);
        });

        cfg.setFileOutConfigList(focList);
        return cfg;
    }

    /**
     * 模板配置
     */
    @Bean
    public TemplateConfig templateConfig() {
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定义输出模板
        //指定自定义模板路径，注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        templateConfig.setMapper("tpl/mapper.java");
        templateConfig.setEntity("tpl/entity.java");
        templateConfig.setService("tpl/service.java");
        templateConfig.setServiceImpl("tpl/serviceImpl.java");
        templateConfig.setController("tpl/controller.java");
        templateConfig.setXml(null);

        return templateConfig;
    }

    /**
     * 策略配置
     */
    @Bean
    public StrategyConfig strategyConfig() {
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setInclude(appConfig.getTableName());
        strategy.setControllerMappingHyphenStyle(false);

        return strategy;
    }
}
